package org.example.ams.service.impl.system;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.example.ams.dao.system.ISysMenuDao;
import org.example.ams.dao.system.ISysUserDao;
import org.example.ams.entity.system.SysMenu;
import org.example.ams.service.system.ISysMenuService;
import org.example.ams.util.SystemUtils;
import org.example.common.constant.RedisConstant;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @author cheval
 */
@Service
public class SysMenuServiceImpl implements ISysMenuService {
    @Autowired
    private ISysMenuDao sysMenuDao;
    @Autowired
    private ISysUserDao sysUserDao;
    @Autowired
    private SystemUtils loginContextUtils;
    @Autowired
    private RedisTemplate redisTemplate;

    /**
     * 获取导航菜单
     * 条件：
     * 1）当前登录用户拥有的菜单
     * 2）菜单类型为 1/2（目录和菜单）
     * 3）未被删除 is_validate = 1
     * 4）未被禁用 enable = 1
     * 5）树形结构
     */
    @Override
    public List<SysMenu> queryMenuTree() {
        Integer userId = loginContextUtils.loginUser().getUserId();
        String key = RedisConstant.NAV_TREE_KEY + ":" +userId;
        // 1. 获取当前登录用户所有的目录和菜单
        // 1.1 首先从缓存中获取，如果缓存中没有再从数据库中获取并添加到缓存中
        List<SysMenu> navMenus = null;
        navMenus = (List<SysMenu>) redisTemplate.opsForValue().get(key);
        if (navMenus == null) {
            List<SysMenu> menuTree = sysMenuDao.selectByUsername(loginContextUtils.loginUser().getUsername());
            navMenus = generateMenuTree(menuTree);
            // 在添加删除修改了菜单之后以及用户管理-角色分配取消分配之后以及角色分配权限取消分配后需要删除缓存
            // 设置超时事件作为缓存更新的兜底方案
            redisTemplate.opsForValue()
                    .set(key,navMenus,RedisConstant.NAV_TREE_CACHE_TIME, RedisConstant.NAV_TREE_CACHE_TIME_UNIT);
        }
        return navMenus;
    }

    /**
     * 生成树形结构的菜单
     *
     * @param sysMenus
     * @return
     */
    private List<SysMenu> generateMenuTree(List<SysMenu> sysMenus) {
        if (sysMenus == null || sysMenus.size() <= 0) {
            return null;
        }
        // 1. 先根据parentId进行分组
        Map<Integer, List<SysMenu>> mapper = sysMenus.stream().sorted((item1, item2) -> {
            return (item2.getOrderNum() == null ? 0 : item2.getOrderNum()) - (item1.getOrderNum() == null ? 0 : item1.getOrderNum());
        }).collect(Collectors.groupingBy(SysMenu::getParentId));
        // 2. 遍历集合，给父菜单设置子菜单
        List<SysMenu> menuTree = sysMenus.stream().sorted((item1, item2) -> {
            return (item2.getOrderNum() == null ? 0 : item2.getOrderNum()) - (item1.getOrderNum() == null ? 0 : item1.getOrderNum());
        }).map(item -> {
            if (mapper.containsKey(item.getMenuId())) {
                item.setChildren(mapper.get(item.getMenuId()));
            }
            return item;
        }).filter(item -> {
            return item.getParentId() == 0;
        }).collect(Collectors.toList());
        return menuTree;
    }


    /**
     * 查询所有权限
     * @return
     */
    @Override
    public List<SysMenu> queryAuthTree() {
        // 先查询缓存，没有查询数据库并添加到缓存中
        List<SysMenu> authTree = null;
        authTree = (List<SysMenu>)redisTemplate.opsForValue().get(RedisConstant.MENU_TREE_KEY);
        if (authTree == null) {
            List<SysMenu> sysMenus = sysMenuDao.selectList(new QueryWrapper<SysMenu>().eq("is_validate", 1));
            authTree = generateMenuTree(sysMenus);
            // 添加删除和修改菜单后需要删除缓存
            redisTemplate.opsForValue()
                    .set(RedisConstant.MENU_TREE_KEY,authTree,RedisConstant.MENU_TREE_CACHE_TIME ,RedisConstant.MENU_TREE_CACHE_TIME_UNIT);
        }
        return authTree;
    }

    /**
     * 分页查询
     * @param currentPage
     * @param pageSize
     * @param params
     * @return
     */
    @Override
    public Map<String,Object> queryByPage(Integer currentPage, Integer pageSize, Map<String, String[]> params){
        QueryWrapper<SysMenu> queryWrapper = new QueryWrapper<SysMenu>();



        queryWrapper.eq("is_validate",1);
        if (params.get("parentId") != null && !"".equals(params.get("parentId"))) {
            queryWrapper.eq("parent_id",params.get("parentId")[0]);
        }


        if (params.get("name") != null && !"".equals(params.get("name"))) {
            queryWrapper.like("name",params.get("name")[0]);
        }


        if (params.get("type") != null && !"".equals(params.get("type"))) {
            queryWrapper.eq("type",params.get("type")[0]);
        }


        if (params.get("isLink") != null && !"".equals(params.get("isLink"))) {
            queryWrapper.eq("is_link",params.get("isLink")[0]);
        }
        queryWrapper.orderByAsc("menu_id");

        Page<SysMenu> page = new Page<SysMenu>(currentPage,pageSize);
        Page<SysMenu> ipage = null;
        ipage = sysMenuDao.selectPage(page, queryWrapper);

        // 做用户id和用户名之间的映射关系，避免双层for循环，降低时间复杂度
        Map<Integer, String> userMap = loginContextUtils.getUserIdToNameMapper();
        // 菜单数据处理
        List<SysMenu> menus = ipage.getRecords();
        for (SysMenu menu : menus) {
            menu.setCreateUserName(userMap.get(menu.getCreateUserId()));
        }


        Map<String,Object> res = new HashMap<>();
        res.put("total",ipage.getTotal());
        res.put("data",menus);
        return res;
    }


    /**
     * 添加
     *
     * @param sysMenu 添加对象
     */
    @Override
    public void save(SysMenu sysMenu) {
        sysMenu.setCreateUserId(loginContextUtils.loginUser().getUserId());
        sysMenu.setCreateTime(new Date());
        sysMenu.setUpdateTime(new Date());
        // 添加菜单
        sysMenuDao.insert(sysMenu);
        // 缓存更新策略 - 删除缓存
        redisTemplate.delete(RedisConstant.NAV_TREE_KEY);
        redisTemplate.delete(RedisConstant.MENU_TREE_KEY);
    }

    /**
     * 修改
     *
     * @param sysMenu 修改对象
     */
    @Override
    public void update(SysMenu sysMenu) {
        sysMenuDao.updateById(sysMenu);
    }

    /**
     * 删除
     *
     * @param ids ids
     */
    @Override
    public void delete(Integer[] ids) {
        sysMenuDao.deleteBatchIds(Arrays.asList(ids));
    }

    @Override
    public List<SysMenu> queryAllDirectory() {
        return sysMenuDao.selectList(new QueryWrapper<SysMenu>().eq("type",0).eq("is_validate",1));
    }

    /**
     * 检查ID是否已经存在
     * @param menuId
     * @return
     */
    @Override
    public boolean checkIDExist(Integer menuId) {
        SysMenu sysMenu = sysMenuDao.selectById(menuId);
        if (sysMenu != null) {
            return true;
        }
        return false;
    }
}

